#include "ctrlserver.h"

/*
 *************************************************************************************************************
 - 函数说明 : 设置串口
 *************************************************************************************************************
 */
void Uart_SetSpeed(int fd, int speed)
{
    int i;
    int status;
    struct termios Opt;
    tcgetattr(fd, &Opt);
    for (i = 0; i < sizeof(speed_arr) / sizeof(int); i++) {
        if (speed == name_arr[i]) {
            tcflush(fd, TCIOFLUSH);
            cfsetispeed(&Opt, speed_arr[i]);
            cfsetospeed(&Opt, speed_arr[i]);
            status = tcsetattr(fd, TCSANOW, &Opt);
            if (status != 0)
                perror("Set Uart Speed");
            return;
        }
        tcflush(fd, TCIOFLUSH);
    }
}

/*
 *************************************************************************************************************
 - 函数说明 : 串口发送16进制
 *************************************************************************************************************
 */
char SendUpdate[23];//1个包头，1个组地址,10*2个灯数据,1个包尾
int count = 0;//更新包收集完10个一起发送
int Uart_SendCmd(LedParameter lparam)
{
#if EN_DEBUG
    printf("count:%d\n",count+1);
#endif
    char buf[100];
    int uart_fd = open(UART_FD, O_RDWR | O_NONBLOCK);
    if (uart_fd == -1) {
        perror("Open "UART_FD" Failed\n");
        return ERROR;
    }
    Uart_SetSpeed(uart_fd,9600);
    if(lparam.pack_type == PACKAGE_START){
        buf[0] = 0xF0;
        buf[1] = lparam.mode;
        buf[2] = 0xF1;
        write(uart_fd,buf,3);
    }else if(lparam.pack_type == PACKAGE_UPDATE)
    {
        if((lparam.id-1) % 10 != count)
            count = 0;
        SendUpdate[(count+1) * 2 ] = lparam.parameter % 16;//取开关、RGB组合值
        SendUpdate[(count+1) * 2 + 1] = lparam.parameter / 16;//取亮度
        if((lparam.id-1) % 10 == count)count++;
        if(count >= 10)//满10个就发送
        {
            SendUpdate[0] = 0xC8 + lparam.mode * 2;//包头
            SendUpdate[1] = (lparam.id -1 ) / 10;//组地址
            SendUpdate[22] = 0xC9 + lparam.mode * 2;//包尾
            count = 0;
            write(uart_fd,SendUpdate,23);//更新
            sleep(1);
        }else{
            close(uart_fd);
            return WAIT;
        }
    }
    close(uart_fd);
    return OK;

}

/* 取子串的函数 */
static char* substr(const char*str,unsigned start, unsigned end)
{
    unsigned n = end - start;
    static char stbuf[256];
    strncpy(stbuf, str + start, n);
    stbuf[n] = '\0';
    return stbuf;
}

/*
 *************************************************************************************************************
 - 函数说明 : 检查数据包正确性
 *************************************************************************************************************
 */
int LED_Check(const char *str)
{
    char *p;
    int len = strlen(str);
    p = substr(str,0,4);
    if(strcmp(p,"AA//"))return ERROR;
    p = substr(str,len-4,len);
    if(strcmp(p,"//BB"))return ERROR;
    return OK;
}

/*
 *************************************************************************************************************
 - 函数说明 : 解包
 *************************************************************************************************************
 */
int LED_Decode(char *net_msg,LedParameter *lparam)
{
    int ret = LED_Check(net_msg);
    if(ret != OK)return ERROR;
    char buf[256],*p,seg[24];
    int nseg = 0,j,value;
    char ch;
    int i = strlen(net_msg);
    p = substr(net_msg,4,i-4);//获得数据

    strcpy(buf,p);
    p = &buf[0];

    while(*p != '\0')
    {
        j = 0;
        while(*p != '/' && *p != '\0')
            seg[j++] = *p++;
        seg[j] = '\0';
        //		printf("seg:%s\n",seg);
        value = atoi(seg);
        switch(nseg)
        {
        case 0:	if(value == 1)lparam->pack_type = PACKAGE_START;//启用包
            else if(value == 2)lparam->pack_type = PACKAGE_UPDATE;//更新包
            break;
        case 1:lparam->mode = value;break;
            break;
        case 2:lparam->id = value;break;
        case 3:lparam->parameter = value;break;
        }
        nseg++;
        if(*p == '/')p++;
    }
#if EN_DEBUG
    printf("PackType:<%d>||Mode:<%d>||ID:<%d>||Parameter:<%d>\n",lparam->pack_type,lparam->mode,lparam->id,lparam->parameter);
#endif
    return OK;
}

/*
  启动摄像头，并向客户端发送视频数据
 */
void onCamera(void)
{
    CvCapture* pCapture = NULL;
    IplImage* frame = NULL;
    IplImage* dst = NULL;
    CvSize dst_cvsize;
    bool loop = true;
    socklen_t addrlen;
    ssize_t i;
    int imagefd , readsize;
    unsigned char image[250*250];
    addrlen = sizeof(struct sockaddr_in);

    //获取摄像头
    while(pCapture == NULL)
        pCapture = cvCreateCameraCapture(0);

    //创建窗口
    //  cvNamedWindow("video", 1);

    frame=cvQueryFrame( pCapture );
    dst_cvsize.width = frame->width * scale;		//目标图像的宽为源图象宽的scale倍
    dst_cvsize.height = frame->height * scale;          //目标图像的高为源图象高的scale倍


    printf(" Begin Loop...\n");

    //显示视屏
    while(loop)
    {
        frame=cvQueryFrame( pCapture );
        if(!frame)
            break;
        dst = cvCreateImage(dst_cvsize,frame->depth,frame->nChannels);
        cvResize(frame,dst,CV_INTER_LINEAR);
        cvSaveImage(tmpFile, dst);

        if((imagefd = open(tmpFile,O_RDONLY,0666)) < 0)
        {
            perror("open error!\n");
        }

        if((readsize = read(imagefd,image,250*250)) < 0)
        {
            perror("read error!\n");
        }
        close(imagefd);

 #if EN_DEBUG
        printf("send to: %s:%d\t\n",(char*) inet_ntoa(sendaddr.sin_addr),ntohs(sendaddr.sin_port));
  #endif

        if(i = (sendto(fd, image, 250*250, 0, (struct sockaddr *) &sendaddr, addrlen)) == -1)
        {
            perror("send unsuccess!");
        }

        loop = stopCamera();

#if EN_DEBUG
        cvShowImage("video",dst);
        char c=cvWaitKey(33);
        if(c==27)
            break;
#endif
    }
    printf("Finish the funciton, waiting destroy memory...\n");
    if(dst != NULL)
        cvReleaseImage(&dst);
    if(frame != NULL)
        cvReleaseImage(&frame);
    if(pCapture != NULL)
        cvReleaseCapture(&pCapture);
}

/*
  关闭摄像头，停止向客户端发送数据
 */
bool stopCamera()
{
    socklen_t addrlen;
    ssize_t i;
    bool loop;

    bzero(msg, sizeof(msg));
    addrlen = sizeof(struct sockaddr_in);

    if ((i = recvfrom(fd, msg, sizeof(msg), 0, (struct sockaddr *) &addr,&addrlen)) != -1)
    {
        //sendto(fd, msg, sizeof(msg), 0, (struct sockaddr *) &addr, addrlen);//原样回发
        if(strcmp(msg,"Stop") == 0)
        {
      #if EN_DEBUG
            printf("%s:%d:%s\t\n",(char*) inet_ntoa(addr.sin_addr),ntohs(addr.sin_port),msg);
       #endif
            loop = false;
        }
    }
    else
    {
        printf("Rec Error\n");
        loop = true;
    }
    return loop;
}

/*
  初始化服务器端
  */
bool initServer()
{
    int flags;
    if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0)
    {
        perror("init socket");
        return false;
    }
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERVER_PORT);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
    {
        perror("UDP bind");
        return false;
    }

    if((flags=fcntl(fd,F_SETFL,0)) < 0)
        perror("fcntl F_SETFL");
    flags |= O_NONBLOCK;
    if(fcntl(fd,F_SETFL,flags) < 0)
        perror("fcntl");

    printf("UDP server start linsten@:\t%d\n",SERVER_PORT);
    return true;
}

/*
 *************************************************************************************************************
 - 函数说明 : UDP监听端口
 *************************************************************************************************************
 */
int UDPLED_Server(void)
{
    int ret;
    socklen_t addrlen;
    int i = 0;

    while( fd < 0 )
        initServer();

    LedParameter lparam;
    while (1)
    {
        bzero(msg, sizeof(msg));
        addrlen = sizeof(struct sockaddr_in);
        if ((i = recvfrom(fd, msg, sizeof(msg), 0, (struct sockaddr *) &addr,&addrlen)) == -1)
        {
            //printf("Rec Error\n");
            continue;
        }
#if EN_DEBUG
        printf("%s:%d:%s \n",(char*) inet_ntoa(addr.sin_addr),ntohs(addr.sin_port),msg);
        //sendto(fd, msg, sizeof(msg), 0, (struct sockaddr *) &addr, addrlen);//原样回发
#endif
        if(strcmp(msg,"Start") == 0)
        {
            sendaddr = addr;
            onCamera();
        }
        ret = LED_Decode(msg,&lparam);
        if(ret == OK){
            ret = Uart_SendCmd(lparam);
#if EN_DEBUG
            if(ret == OK)printf("Send Cmd OK\n");
#endif
        }
    }

}

int main(void)
{
    printf("<<UDPLED Start>>\n"); /* prints !!!Hello World!!! */
    bzero(msg, sizeof(msg));
    bzero(&sendaddr, sizeof(struct sockaddr_in));
    UDPLED_Server();
    return 0;
}
